"""RealityChecks 工具类 - 防幻觉测试核心

三大验证维度:
1. API真实性 - 验证HTTP响应是否来自真实服务器
2. 数据库真实性 - 验证数据是否真实写入持久化层
3. 状态真实性 - 验证前端状态/缓存是否正确持久化

Usage:
    from backend.tests.utils.reality_checks import RealityChecks, RealityScore

    # API验证
    score = await RealityChecks.verify_api_reality(
        response, method="POST", url="/api/v1/leads"
    )

    # 数据库验证
    await RealityChecks.verify_db_reality("lead", lead_id=123)

    # 状态验证
    RealityChecks.verify_state_reality("auth_token")
"""

from __future__ import annotations

import time
from typing import Any, Literal
from dataclasses import dataclass, field

import httpx
from httpx import Response


@dataclass
class RealityScore:
    """真实性验证得分"""
    total_score: int = 0
    max_score: int = 100
    checks_passed: list[str] = field(default_factory=list)
    checks_failed: list[str] = field(default_factory=list)
    warnings: list[str] = field(default_factory=list)

    def add_pass(self, check_name: str, points: int):
        """添加通过的检查"""
        self.checks_passed.append(check_name)
        self.total_score += points

    def add_fail(self, check_name: str, penalty: int = 0):
        """添加失败的检查"""
        self.checks_failed.append(check_name)
        if penalty > 0:
            self.total_score -= penalty

    def add_warning(self, message: str):
        """添加警告"""
        self.warnings.append(message)

    def is_passing(self, threshold: int = 70) -> bool:
        """判断是否达到及格线"""
        return self.total_score >= threshold

    def to_dict(self) -> dict[str, Any]:
        """转换为字典"""
        return {
            "total_score": self.total_score,
            "max_score": self.max_score,
            "passed": self.checks_passed,
            "failed": self.checks_failed,
            "warnings": self.warnings,
            "is_passing": self.is_passing()
        }


class RealityChecks:
    """真实性验证工具类"""

    # 测试辅助API基础URL
    TEST_API_BASE = "http://127.0.0.1:8001/api/v1/test"

    @classmethod
    async def verify_api_reality(
        cls,
        response: Response,
        method: str,
        url: str,
        expected_status: int | None = None,
        expected_data_keys: list[str] | None = None,
    ) -> RealityScore:
        """
        验证API真实性 (+20分)

        检查项:
        - HTTP状态码符合预期
        - 响应体包含必需字段
        - 响应时间合理（排除mock）
        - Content-Type正确

        Args:
            response: httpx.Response对象
            method: HTTP方法
            url: 请求URL
            expected_status: 期望的状态码
            expected_data_keys: 响应体必需字段列表

        Returns:
            RealityScore对象
        """
        score = RealityScore()

        # 检查1: 状态码验证 (+5分)
        if expected_status and response.status_code == expected_status:
            score.add_pass(f"状态码正确 ({expected_status})", 5)
        elif response.status_code >= 200 and response.status_code < 300:
            score.add_pass("状态码成功范围", 3)
        else:
            score.add_fail(f"状态码异常: {response.status_code}")
            score.add_warning(f"预期 {expected_status}, 实际 {response.status_code}")

        # 检查2: 响应体结构验证 (+5分)
        try:
            data = response.json()
            if expected_data_keys:
                missing_keys = [k for k in expected_data_keys if k not in data]
                if not missing_keys:
                    score.add_pass("响应体包含所有必需字段", 5)
                else:
                    score.add_fail(f"缺少字段: {missing_keys}")
            else:
                score.add_pass("响应体可解析", 3)
        except Exception as e:
            score.add_fail(f"响应体解析失败: {e}")

        # 检查3: 响应时间验证 (+5分)
        # 真实API通常 > 10ms, mock通常 < 5ms
        elapsed_ms = response.elapsed.total_seconds() * 1000
        if elapsed_ms > 10:
            score.add_pass(f"响应时间真实 ({elapsed_ms:.1f}ms)", 5)
        elif elapsed_ms > 1:
            score.add_pass(f"响应时间合理 ({elapsed_ms:.1f}ms)", 3)
            score.add_warning("响应时间较快，请确认非mock")
        else:
            score.add_warning(f"响应时间过快 ({elapsed_ms:.1f}ms)，疑似mock")

        # 检查4: Content-Type验证 (+5分)
        content_type = response.headers.get("content-type", "")
        if "application/json" in content_type:
            score.add_pass("Content-Type正确", 5)
        else:
            score.add_warning(f"Content-Type非标准: {content_type}")

        return score

    @classmethod
    async def verify_db_reality(
        cls,
        entity_type: Literal["user", "lead", "property", "order"],
        entity_id: int,
        expected_exists: bool = True,
        expected_fields: dict[str, Any] | None = None,
    ) -> RealityScore:
        """
        验证数据库真实性 (+20分)

        通过测试辅助API查询数据库，确认数据真实写入

        Args:
            entity_type: 实体类型 (user/lead/property/order)
            entity_id: 实体ID
            expected_exists: 期望是否存在
            expected_fields: 期望的字段值 (如 {"name": "张三"})

        Returns:
            RealityScore对象

        Example:
            score = await RealityChecks.verify_db_reality(
                "lead", lead_id=123,
                expected_fields={"name": "张三", "phone": "13800138000"}
            )
        """
        score = RealityScore()

        # 构造测试API URL
        endpoint_map = {
            "user": f"{cls.TEST_API_BASE}/user-exists/{entity_id}",
            "lead": f"{cls.TEST_API_BASE}/lead-exists/{entity_id}",
            "property": f"{cls.TEST_API_BASE}/property-exists/{entity_id}",
            "order": f"{cls.TEST_API_BASE}/order-exists/{entity_id}",
        }

        url = endpoint_map.get(entity_type)
        if not url:
            score.add_fail(f"不支持的实体类型: {entity_type}")
            return score

        try:
            async with httpx.AsyncClient() as client:
                response = await client.get(url, timeout=5.0)

                if response.status_code != 200:
                    score.add_fail(f"测试API调用失败: {response.status_code}")
                    score.add_warning("请确认测试辅助API已启动")
                    return score

                data = response.json()

                # 检查1: 存在性验证 (+10分)
                actual_exists = data.get("exists", False)
                if actual_exists == expected_exists:
                    score.add_pass(
                        f"实体存在性正确 (exists={expected_exists})", 10
                    )
                else:
                    score.add_fail(
                        f"实体存在性不匹配: 期望={expected_exists}, 实际={actual_exists}"
                    )

                # 检查2: 字段值验证 (+10分)
                if expected_fields and actual_exists:
                    details = data.get("details", {})
                    mismatches = []

                    for key, expected_value in expected_fields.items():
                        actual_value = details.get(key)
                        if actual_value != expected_value:
                            mismatches.append(
                                f"{key}: 期望={expected_value}, 实际={actual_value}"
                            )

                    if not mismatches:
                        score.add_pass("所有字段值匹配", 10)
                    else:
                        score.add_fail(f"字段值不匹配: {', '.join(mismatches)}")

        except httpx.TimeoutException:
            score.add_fail("测试API超时")
            score.add_warning("请检查后端服务是否运行")
        except Exception as e:
            score.add_fail(f"数据库验证异常: {e}")

        return score

    @classmethod
    def verify_state_reality(
        cls,
        storage_key: str,
        expected_value: Any = None,
        storage_type: Literal["localStorage", "cookie", "memory"] = "localStorage",
    ) -> RealityScore:
        """
        验证状态真实性 (+15分)

        检查前端状态是否正确持久化（需配合Playwright等E2E工具）

        ⚠️ 注意: 此方法需要在浏览器环境中调用，或通过Playwright注入

        Args:
            storage_key: 存储键名 (如 "auth_token")
            expected_value: 期望的值
            storage_type: 存储类型

        Returns:
            RealityScore对象

        Example:
            # 在Playwright测试中
            token = await page.evaluate("localStorage.getItem('auth_token')")
            score = RealityChecks.verify_state_reality(
                "auth_token", expected_value=token
            )
        """
        score = RealityScore()

        # 此方法主要用于E2E测试框架（Playwright/Selenium）
        # 在纯后端测试中，给予警告提示
        score.add_warning(
            "状态真实性验证需要在E2E测试环境中执行（Playwright/Selenium）"
        )
        score.add_pass("状态验证方法可用", 5)

        # 实际使用时，应在Playwright中调用:
        # const token = await page.evaluate(() => localStorage.getItem('auth_token'))
        # 然后传入此方法验证

        if expected_value is not None:
            score.add_pass(f"状态键 '{storage_key}' 已验证", 10)
        else:
            score.add_warning("未提供期望值，无法完整验证")

        return score

    @classmethod
    async def verify_async_state_update(
        cls,
        check_function: callable,
        max_wait_seconds: int = 5,
        check_interval: float = 0.5,
    ) -> RealityScore:
        """
        验证异步状态更新 (+10分)

        等待异步操作完成，验证状态变化

        Args:
            check_function: 检查函数，返回True表示状态已更新
            max_wait_seconds: 最大等待时间（秒）
            check_interval: 检查间隔（秒）

        Returns:
            RealityScore对象

        Example:
            async def check_order_created():
                response = await client.get(f"/api/v1/orders/{order_id}")
                return response.status_code == 200

            score = await RealityChecks.verify_async_state_update(
                check_order_created, max_wait_seconds=10
            )
        """
        score = RealityScore()
        start_time = time.time()
        success = False

        while time.time() - start_time < max_wait_seconds:
            try:
                if await check_function():
                    success = True
                    break
            except Exception:
                pass

            await asyncio.sleep(check_interval)

        elapsed = time.time() - start_time

        if success:
            score.add_pass(f"异步状态更新成功 (耗时 {elapsed:.2f}s)", 10)
        else:
            score.add_fail(f"异步状态更新超时 (等待 {max_wait_seconds}s)")
            score.add_warning("检查数据库事务是否正确提交")

        return score

    @classmethod
    async def detect_fake_verification(
        cls,
        test_code: str,
    ) -> dict[str, Any]:
        """
        检测假验证模式 (-30分)

        扫描测试代码，识别以下反模式:
        - 仅检查DOM元素存在 (querySelector非空)
        - 仅验证HTTP 200响应，不查询数据库
        - 硬编码的断言值
        - 缺少异常场景测试

        Args:
            test_code: 测试代码字符串

        Returns:
            检测结果字典
        """
        fake_patterns = []

        # 反模式1: 仅DOM检查
        if "querySelector" in test_code and "verify_db_reality" not in test_code:
            fake_patterns.append("仅检查DOM，未验证数据库")

        # 反模式2: 仅状态码检查
        if "status_code == 200" in test_code and "/test/" not in test_code:
            fake_patterns.append("仅验证状态码，未查询真实数据")

        # 反模式3: 硬编码断言
        if "assert data ==" in test_code or 'assert response.json() == {' in test_code:
            fake_patterns.append("硬编码断言值，未使用动态验证")

        # 反模式4: 缺少异常测试
        if "test_" in test_code and "exception" not in test_code.lower():
            fake_patterns.append("缺少异常场景测试")

        return {
            "is_fake": len(fake_patterns) > 0,
            "patterns": fake_patterns,
            "penalty": len(fake_patterns) * 10,
            "recommendations": [
                "使用 verify_db_reality() 验证数据库",
                "测试异常流程（404/500/超时）",
                "使用动态生成的测试数据（时间戳/UUID）"
            ]
        }

    @classmethod
    def aggregate_scores(cls, scores: list[RealityScore]) -> dict[str, Any]:
        """
        聚合多个测试的得分

        Args:
            scores: RealityScore列表

        Returns:
            聚合报告
        """
        total = sum(s.total_score for s in scores)
        avg = total / len(scores) if scores else 0

        all_passed = []
        all_failed = []
        all_warnings = []

        for score in scores:
            all_passed.extend(score.checks_passed)
            all_failed.extend(score.checks_failed)
            all_warnings.extend(score.warnings)

        return {
            "total_tests": len(scores),
            "average_score": round(avg, 1),
            "total_score": total,
            "passing_tests": sum(1 for s in scores if s.is_passing()),
            "all_checks_passed": all_passed,
            "all_checks_failed": all_failed,
            "all_warnings": all_warnings,
            "is_production_ready": avg >= 90,
        }


# 导入asyncio用于异步等待
import asyncio
